home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * FILE
- * prepqual
- *
- * DESCRIPTION
- * Routines for preprocessing the parse tree qualification
- *
- */
- /* RcsId ("$Header: /private/postgres/src/planner/prep/RCS/prepqual.c,v 1.22 1992/08/03 23:49:49 joey Exp $"); */
-
- /*
- * EXPORTS
- * preprocess-qualification
- */
-
- #include "nodes/pg_lisp.h"
-
- #include "planner/clauses.h"
- #include "planner/internal.h"
- #include "planner/clause.h"
- #include "planner/clauses.h"
- #include "planner/prepqual.h"
- #include "utils/lsyscache.h"
-
- /*
- * preprocess-qualification
- *
- * Driver routine for modifying the parse tree qualification.
- *
- * Returns the new base qualification and the existential qualification
- * in a list.
- *
- */
-
- /* .. init-query-planner */
-
- LispValue
- preprocess_qualification (qual,tlist)
- LispValue qual,tlist ;
- {
- LispValue cnf_qual = cnfify (qual, true);
- LispValue existential_qual =
- update_clauses (lispCons (lispInteger((int)_query_result_relation_),
- update_relations (tlist)),
- cnf_qual,(LispValue)_query_command_type_);
- if ( existential_qual )
- return (lispCons (set_difference (cnf_qual,existential_qual),
- lispCons(existential_qual,LispNil)));
- else
- return (lispCons (cnf_qual,lispCons(existential_qual,LispNil)));
-
- } /* function end */
-
- /* =======================
- * CNF CONVERSION ROUTINES
- * =======================
- */
-
- /*
- * NOTES:
- * The basic algorithms for normalizing the qualification are taken
- * from ingres/source/qrymod/norml.c
- *
- * Remember that the initial qualification may consist of ARBITRARY
- * combinations of clauses. In addition, before this routine is called,
- * the qualification will contain explicit "AND"s.
- *
- */
-
- /*
- * cnfify
- *
- * Convert a qualification to conjunctive normal form by applying
- * successive normalizations.
- *
- * Returns the modified qualification with an extra level of nesting.
- *
- * If 'removeAndFlag' is true then it removes the explicit ANDs.
- *
- * NOTE: this routine is called by the planner (removeAndFlag = true)
- * and from the rule manager (removeAndFlag = false).
- *
- */
-
- /* .. preprocess-qualification, print_parse */
-
- LispValue
- cnfify (qual, removeAndFlag)
- LispValue qual ;
- bool removeAndFlag;
- {
- LispValue newqual = LispNil;
-
- if ( consp (qual) ) {
- newqual = find_nots (pull_args (qual));
- newqual = normalize (pull_args (newqual));
- newqual = qualcleanup (pull_args (newqual));
- newqual = pull_args (newqual);;
-
- if (removeAndFlag) {
- if(and_clause (newqual))
- newqual=remove_ands(newqual);
- else
- newqual=remove_ands(make_andclause(lispCons(newqual,LispNil)));
- }
- }
- else if ( !lispNullp(qual) )
- newqual = lispCons(qual, LispNil);
-
- return (newqual);
-
- } /* function end */
-
- /*
- * pull-args
- *
- * Given a qualification, eliminate nested 'and' and 'or' clauses.
- *
- * Returns the modified qualification.
- *
- */
-
- /* .. cnfify, pull-args */
-
- LispValue
- pull_args (qual)
- LispValue qual ;
- {
- if(null (qual))
- return (LispNil);
- else
- if (is_clause (qual))
- return(make_clause (get_op (qual),
- lispCons(pull_args ((LispValue)get_leftop (qual)),
- lispCons(pull_args ((LispValue)get_rightop(qual)),
- LispNil))));
-
- else
- if (and_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach (temp,get_andclauseargs(qual))
- t_list = nappend1 (t_list, pull_args(CAR(temp)));
- return (make_andclause (pull_ands (t_list)));
- }
- else
- if (or_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach (temp,get_orclauseargs(qual))
- t_list = nappend1 (t_list, pull_args(CAR(temp)));
- return (make_orclause (pull_ors (t_list)));
- }
- else
- if (not_clause (qual))
- return (make_notclause (pull_args (get_notclausearg (qual))));
- else
- return (qual);
- } /* function end */
-
- /*
- * pull-ors
- *
- * Pull the arguments of an 'or' clause nested within another 'or'
- * clause up into the argument list of the parent.
- *
- * Returns the modified list.
- */
-
- /* .. distribute-args, pull-args, pull-ors */
-
- LispValue
- pull_ors (orlist)
- LispValue orlist ;
- {
- if(null (orlist))
- return (LispNil);
- else
- if (or_clause (CAR (orlist)))
- return (pull_ors (nconc(CopyObject(get_orclauseargs(CAR(orlist))),
- CopyObject(CDR (orlist)))));
- else
- return (lispCons (CAR (orlist),pull_ors (CDR (orlist))));
- } /* function end */
-
- /*
- * pull-ands
- *
- * Pull the arguments of an 'and' clause nested within another 'and'
- * clause up into the argument list of the parent.
- *
- * Returns the modified list.
- */
-
- /* .. pull-ands, pull-args */
-
- LispValue
- pull_ands (andlist)
- LispValue andlist ;
- {
- if(null (andlist))
- return (LispNil);
- else
- if (and_clause (CAR (andlist)))
- return (pull_ands(nconc(CopyObject(get_andclauseargs(CAR(andlist))),
- CopyObject(CDR (andlist)))));
- else
- return (lispCons (CAR (andlist),pull_ands (CDR (andlist))));
- } /* function end */
-
- /*
- * find-nots
- *
- * Traverse the qualification, looking for 'not's to take care of.
- * For 'not' clauses, remove the 'not' and push it down to the clauses'
- * descendants.
- * For all other clause types, simply recurse.
- *
- * Returns the modified qualification.
- *
- */
-
- /* .. cnfify, find-nots, push-nots */
-
- LispValue
- find_nots (qual)
- LispValue qual ;
- {
- if(null (qual))
- return (LispNil);
- else
- if (is_clause (qual))
- return (make_clause (get_op (qual),
- lispCons(find_nots ((LispValue)get_leftop (qual)),
- lispCons( find_nots ((LispValue)get_rightop (qual)),
- LispNil))));
- else
- if (and_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach (temp,(get_andclauseargs(qual))) {
- t_list = nappend1(t_list,find_nots(CAR(temp)));
- }
-
- return (make_andclause (t_list));
- } else
- if (or_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach (temp,get_orclauseargs(qual)) {
- t_list = nappend1(t_list,find_nots(CAR(temp)));
- }
- return (make_orclause (t_list));
- } else
- if (not_clause (qual))
- return (push_nots (get_notclausearg (qual)));
- else
- return (qual);
- } /* function end */
-
- /*
- * push-nots
- *
- * Negate the descendants of a 'not' clause.
- *
- * Returns the modified qualification.
- *
- */
-
- /* .. find-nots, push-nots */
-
- LispValue
- push_nots (qual)
- LispValue qual ;
- {
- if(null (qual))
- return (LispNil);
- else
- /* Negate an operator clause if possible: */
- /* ("NOT" (< A B)) => (> A B) */
- /* Otherwise, retain the clause as it is (the 'not' can't be pushed */
- /* down any farther). */
-
- if(is_clause (qual)) {
- LispValue oper = get_op (qual);
- ObjectId negator = get_negator (get_opno ((Oper)oper));
- if(negator)
- {
- Oper op = (Oper) MakeOper (negator,
- InvalidObjectId,
- get_oprelationlevel ((Oper)oper),
- get_opresulttype ((Oper)oper), NULL, NULL);
- op->op_fcache = (FunctionCache *) NULL;
- return (lispCons((LispValue)op, lispCons((LispValue)get_leftop (qual),
- lispCons((LispValue)get_rightop (qual),
- LispNil))));
- }
- else
- return (make_notclause (qual));
- }
- else
- /* Apply DeMorgan's Laws: */
- /* ("NOT" ("AND" A B)) => ("OR" ("NOT" A) ("NOT" B)) */
- /* ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B)) */
- /* i.e., continue negating down through the clause's descendants. */
- if (and_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach(temp,get_andclauseargs(qual)) {
- t_list = nappend1(t_list,push_nots(CAR(temp)));
- }
- return (make_orclause (t_list));
- }
- else
- if (or_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach(temp,get_orclauseargs(qual)) {
- t_list = nappend1(t_list,push_nots(CAR(temp)));
- }
- return (make_andclause (t_list));
- }
- else
- /* Another 'not' cancels this 'not', so eliminate the 'not' and */
- /* stop negating this branch. */
- if (not_clause (qual))
- return (find_nots (get_notclausearg (qual)));
- else
- /* We don't know how to negate anything else, */
- /* place a 'not' at this */
- /* level. */
- return (make_notclause (qual));
-
- } /* function end */
-
- /*
- * normalize
- *
- * Given a qualification tree with the 'not's pushed down, convert it
- * to a tree in CNF by repeatedly applying the rule:
- * ("OR" A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
- * bottom-up.
- * Note that 'or' clauses will always be turned into 'and' clauses.
- *
- * Returns the modified qualification.
- *
- */
-
- /* .. cnfify, normalize
- */
- LispValue
- normalize (qual)
- LispValue qual ;
- {
- if(null (qual))
- return (LispNil);
- else
- if (is_clause (qual))
- return (make_clause (get_op (qual),
- lispCons(normalize ((LispValue)get_leftop (qual)),
- lispCons( normalize ((LispValue)get_rightop (qual)),
- LispNil))));
- else
- if (and_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- foreach (temp,get_andclauseargs(qual)) {
- t_list = nappend1(t_list,normalize(CAR(temp)));
- }
- return (make_andclause (t_list));
- } else
- if (or_clause (qual)) {
- /* XXX - let form, maybe incorrect */
- LispValue orlist = LispNil;
- LispValue temp = LispNil;
- LispValue has_andclause = LispNil;
-
- foreach(temp,get_orclauseargs(qual)) {
- orlist = nappend1(orlist,normalize(CAR(temp)));
- }
- temp = LispNil;
- /* XXX was some */
- foreach (temp,orlist) {
- if ( and_clause (CAR(temp)) )
- has_andclause = LispTrue;
- if (has_andclause == LispTrue)
- break;
- }
- if(has_andclause == LispTrue)
- return (make_andclause (or_normalize (orlist)));
- else
- return (make_orclause (orlist));
-
- } else
- if (not_clause (qual))
- return (make_notclause (normalize (get_notclausearg (qual))));
- else
- return (qual);
-
- } /* function end */
-
- /*
- * or-normalize
- *
- * Given a list of exprs which are 'or'ed together, distribute any
- * 'and' clauses.
- *
- * Returns the modified list.
- *
- */
-
- /* .. distribute-args, normalize, or-normalize */
-
- LispValue
- or_normalize (orlist)
- LispValue orlist ;
- {
- if ( consp (orlist) ) {
- LispValue distributable = LispNil;
- LispValue new_orlist = LispNil;
- LispValue temp = LispNil;
-
- foreach(temp,orlist) {
- if (and_clause(CAR(temp)) )
- distributable = CAR(temp);
- }
- if (distributable)
- new_orlist = LispRemove(distributable,orlist);
-
- if(new_orlist)
- return (or_normalize (lispCons (distribute_args
- (CAR (new_orlist),
- get_andclauseargs (distributable)),
- CDR (new_orlist))));
-
- else
- return (orlist);
-
- }
- return(NULL);
- } /* function end */
-
- /*
- * distribute-args
- *
- * Create new 'or' clauses by or'ing 'item' with each element of 'args'.
- * E.g.: (distribute-args A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
- *
- * Returns an 'and' clause.
- *
- */
-
- /* .. or-normalize */
-
- LispValue
- distribute_args (item,args)
- LispValue item,args ;
- {
- LispValue or_list = LispNil;
- LispValue n_list = LispNil;
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
-
- if(null (args))
- return (item);
-
- foreach (temp,args) {
- n_list = or_normalize(pull_ors(lispCons(item,
- lispCons(CAR(temp),LispNil))));
- or_list = make_orclause(n_list);
- t_list = nappend1(t_list,or_list);
- }
- return (make_andclause (t_list));
-
- } /* function end */
-
- /*
- * qualcleanup
- *
- * Fix up a qualification by removing duplicate entries (left over from
- * normalization), and by removing 'and' and 'or' clauses which have only
- * one valid expr (e.g., ("AND" A) => A).
- *
- * Returns the modified qualfication.
- *
- */
-
- /* .. qualcleanup, cnfify */
-
- LispValue
- qualcleanup (qual)
- LispValue qual ;
- {
- if(null (qual))
- return (LispNil);
- else
- if (is_clause (qual))
- return (make_clause (get_op (qual),
- lispCons(qualcleanup ((LispValue)get_leftop (qual)),
- lispCons( qualcleanup((LispValue)get_rightop(qual)),
- LispNil))));
- else
- if (and_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
- LispValue new_and_args = LispNil;
-
- foreach(temp,get_andclauseargs(qual))
- t_list = nappend1(t_list,qualcleanup(CAR(temp)));
- new_and_args = remove_duplicates (t_list,equal);
-
- if(length (new_and_args) > 1)
- return (make_andclause (new_and_args));
- else
- return (CAR (new_and_args));
- }
- else
- if (or_clause (qual)) {
- LispValue temp = LispNil;
- LispValue t_list = LispNil;
- LispValue new_or_args = LispNil;
-
- foreach (temp,get_orclauseargs(qual))
- t_list = nappend1(t_list,qualcleanup(CAR(temp)));
- new_or_args = remove_duplicates (t_list,equal);
-
- if(length (new_or_args) > 1)
- return (make_orclause (new_or_args));
- else
- return (CAR (new_or_args));
- } else
- if (not_clause (qual))
- return (make_notclause (qualcleanup
- (get_notclausearg (qual))));
- else
- return (qual);
- } /* function end */
-
- /*
- * remove-ands
- *
- * Remove the explicit "AND"s from the qualification:
- * ("AND" A B) => (A B)
- *
- * RETURNS : qual
- * MODIFIES: qual
- */
-
- /* .. cnfify, remove-ands */
-
- LispValue
- remove_ands (qual)
- LispValue qual ;
- {
- LispValue t_list = LispNil;
-
- if(null (qual))
- return (LispNil);
- if (is_clause (qual))
- return (make_clause (get_op (qual),
- lispCons(remove_ands ((LispValue)get_leftop (qual)),
- lispCons(remove_ands((LispValue)get_rightop(qual)),
- LispNil))));
- if (and_clause (qual)) {
- LispValue temp = LispNil;
- foreach (temp,get_andclauseargs(qual))
- t_list = nappend1(t_list,remove_ands(CAR(temp)));
- return(t_list);
- } else
- if (or_clause (qual)) {
- LispValue temp = LispNil;
- foreach (temp,get_orclauseargs(qual))
- t_list = nappend1(t_list,remove_ands(CAR(temp)));
- return (make_orclause (t_list));
- } else
- if (not_clause (qual))
- return (make_notclause (remove_ands
- (get_notclausearg (qual))));
- else
- return (qual);
- } /* function end */
-
- /* ==========================
- * EXISTENTIAL QUALIFICATIONS
- * ==========================
- */
-
- /*
- * update-relations
- *
- * Returns the range table indices (i.e., varnos) for all relations which
- * are referenced in the target list.
- *
- */
-
- /* .. preprocess-qualification */
-
- LispValue
- update_relations (tlist)
- LispValue tlist ;
- {
- LispValue xtl = LispNil;
- LispValue var = LispNil;
- LispValue t_list1 = LispNil; /* used in mapcan */
- LispValue t_list2 = LispNil;
-
- /* was mapCAR nested with mapcan
- foreach(xtll,tlist)
- t_list1 = nconc (t_list1,pull_var_clause(get_expr(CAR(xtl))));
- foreach(var,t_list1)
- t_list2 = nappend1(t_list2,get_varno(CAR(var)));
- return(remove_duplicates (t_list2));
-
- XXX - fix me after "retrieve x=1" works
- by uncommenting the code above and removing the return below
-
- */
- return(LispNil);
- } /* function end */
-
- /*
- * update-clauses
- *
- * Returns those qualifier clauses which reference ONLY non-update
- * relations, i.e., those that are not referenced in the targetlist
- *
- * A var node is existential iff its varno
- * (1) does not reference a relation which is referenced
- * in the target list, and
- * (2) is a number (non-numbers are references to internal
- * results, etc., which are non-existential).
- *
- * Note that clauses without any vars are considered to
- * be existential.
- *
- */
- LispValue
- update_clauses (update_relids,qual,command)
- LispValue update_relids,qual,command ;
- {
- return (LispNil);
- }
-
- /* XXX Close but no cigar. Turn it off for now.
- * .. preprocess-qualification
- * (defun update-clauses (update-relids qual command)
- * #+opus43 (declare (special update-relids))
- * (case command
- * (delete
- * (collect #'(lambda (clause)
- * #+opus43 (declare (special update-relids))
- * (every #'(lambda (var)
- * #+opus43 (declare (special update-relids))
- * (and (var-p var)
- * (integerp (get_varno var))
- * (not (member (get_varno var)
- * update-relids))))
- * (pull_var_clause clause)))
- * qual))))
- */
-